﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Drawing;

using ThingyCloudWPF.Creatures.Properties;
using ThingyCloudWPF.Network;
using ThingyCloudWPF.Network.Messages;
using ThingyCloudWPF.World;

namespace ThingyCloudWPF.Creatures
{
	public class Thingy : Creature
	{
		/// <summary>
		/// Designates whether the thingy has been killed or not
		/// </summary>
		private bool killed = false;

		/// <summary>
		/// The thread that the thingy runs in
		/// </summary>
		private Thread thread;

		/// <summary>
		/// A reference back to the scene. We need it to know the avatars that are available.
		/// </summary>
		private Scene scene;

		/// <summary>
		/// Helper Property that casts the base creature properties to thingy properties, since that is what we need here
		/// </summary>
		private new ThingyProperties Properties
		{
			get
			{
				return base.Properties as ThingyProperties;
			}
		}

		/// <summary>
		/// The speed factor of the thingy. Comes from the properties.
		/// </summary>
		public int Speed
		{
			get
			{
				return Properties.Speed;
			}
		}
		
		/// <summary>
		/// Publicly gets or privately sets the variant for this thingy
		/// </summary>
		public Variant Variant
		{
			get;
			private set;
		}

		/// <summary>
		/// Create a new thingy, based on the next properties. For a thingy to start living, Birth() should
		/// still be invoked after creating it.
		/// </summary>
		public Thingy(Scene scene) : base()
		{
			this.scene = scene;

			SetProperties(PropertiesManager.Instance.GetRandomThingyProperties());

			// Pick a random variant ID
			Random random = new Random(DateTime.Now.Millisecond);
			int idx = Math.Min(random.Next(0, Properties.Variants.Count), Properties.Variants.Count - 1);
			Variant = Properties.Variants[idx];

			// Prepare the thread
			thread = new Thread(new ThreadStart(Live));
		}

		/// <summary>
		/// Kills the thingy
		/// </summary>
		public void Die()
		{
			// Kill and wait for thread
			killed = true;
			thread.Join();

			//Say("I guess my time has come. Live long and prosper.");
		}

		/// <summary>
		/// When the thingy has to be born!
		/// </summary>
		public void Birth()
		{
			//Say("Hello, World!");

			// Random number generators
			Random rX = new Random((int)DateTime.Now.Ticks);
			Random rY = new Random(rX.Next(0, Int32.MaxValue));
			Random rD = new Random(rY.Next(0, Int32.MaxValue));
			Thread.Sleep(250);

			// Be born somewhere
			X = (double)rX.Next(0, 1000000000) / 1000000000.0;
			Y = (double)rY.Next(0, 1000000000) / 1000000000.0;
			Direction = rD.Next(0, 360);

			// Now we're ready to start!
			thread.Start();
		}

		/// <summary>
		/// Invoked when it is created, should handle all the alive parts of the thingy
		/// </summary>
		private void Live()
		{
			//Say(string.Format("I was born on {1} at ({2},{3})! P.ID={4} V.ID={5}", ID, DateTime.Now, X, Y, Properties.ID, Variant.ID));

			// Run as long as we are allowed to
			while (!killed)
			{
				// Get all avatars that are in range of this thingy
				List<Avatar> avatars = scene.GetAvatars(X, Y, 400);

				// Order by weight
				var ordered = from a in avatars
							  where a.Weight > 1
							  orderby a.Weight descending
							  select a;

				Avatar heaviest = null;
				try
				{
					heaviest = ordered.First();
				}
				catch
				{
					heaviest = null;
				}
				
				// If there is an avatar with weight>1, then pick the avatar with the largest weight and move to that one
				if (heaviest != null)// ordered.Count() > 0)
				{
					// Calculate the angle to that avatar and move towards it
					Direction = (int)Globals.rad2deg(Math.Atan2(Y - heaviest.Y, X - heaviest.X));
				}
				// No avatars with weight>1, move away from them!
				else if (avatars.Count > 0)
				{
					/*
					// Determine where to go, we want to move away from the avatars!
					double anglesum = 0;
					foreach (Avatar a in avatars)
						anglesum += Globals.rad2deg(Math.Atan2(Y - a.Y, X - a.X));

					// Determine the new direction where to go
					Direction = ((int)anglesum + 180) % 360;
					*/
				}
				// No avatars at all, move randomly
				else
				{
					// Change direction possibly
					Random r = new Random();
					if (r.Next(0, 100) > 90)
					{
						// Even property id creatures go left, others go right
						if (Properties.ID % 2 == 0) Direction += 2;
						else Direction -= 1;
					}
				}

				// Calculate the delta value based on the creature's speed
				double delta = (double)Speed / 1000.0;

				// Calculate X and Y deltas
				double dx = Math.Cos(Globals.deg2rad(Direction)) * delta;
				double dy = Math.Sin(Globals.deg2rad(Direction)) * delta;

				// New X and Y positions
				X += dx;
				Y -= dy; // substract, because our (0,0) is in the upper left in stead of lower left

				// Stay within bounds
				if (X > 1) X -= 1;
				if (X < 0) X += 1;
				if (Y > 1) Y -= 1;
				if (Y < 0) Y += 1;

				//Say(string.Format("I'm at ({0},{1}) looking at {2} degrees", X, Y, Direction));
				
				// Now we can create a message that says that the thingy has moved
				ThingyMoveMessage message = new ThingyMoveMessage(this, X, Y, Direction);
				MessageServer.Instance.Queue(message);

				// Work at 30 fps
				Thread.Sleep(1000 / 30);
			}

			//Say("I was murdered!");
		}

		/// <summary>
		/// 
		/// </summary>
		public Rectangle GetBoundingRect()
		{
			Rectangle r = new Rectangle((int)Math.Max(0, Globals.ToAbsX(X) - 50), (int)Math.Max(0, Globals.ToAbsY(Y) - 50), 100, 100);
			return r;
		}

		/// <summary>
		/// Stringual representation of the thingy
		/// </summary>
		public override string ToString()
		{
			return string.Format(
					"Thingy: ID={0} Variant.ID={1} Variant.Animation={2} Speed={3} X={4} Y={5} Direction={6}",
					ID,
					Variant.ID,
					Variant.Animation,
					Speed,
					Globals.ToAbsX(X),
					Globals.ToAbsY(Y),
					Direction
					);
		}
	}
}
